﻿#define _CRT_SECURE_NO_WARNINGS 1

#include "Snake.h"

//设置光标位置
void SetPos(short x, short y)
{
	COORD pos = { x, y };
	HANDLE hOutput = NULL;

	//获取标准输出的句柄(⽤来标识不同设备的数值) 
	hOutput = GetStdHandle(STD_OUTPUT_HANDLE);

	//设置标准输出上光标的位置为pos 
	SetConsoleCursorPosition(hOutput, pos);
}

//打印欢迎界面
void Welcome()
{
	SetPos(40, 12);
	printf("欢迎来到贪吃蛇小游戏");

	SetPos(42, 18);
	system("pause");

	//清理界面
	system("cls");

	//打印按键功能信息
	SetPos(8, 12);
	printf("↑  ↓  ←  → 分别控制蛇的上下左右移动， F3为加速，F4为减速, Esc 退出， Space 暂停");

	SetPos(35, 15);
	printf("加速后的食物分数会更高！！！");

	SetPos(40, 20);
	system("pause");
	system("cls");
}

//打印墙体
void CreatWall()
{
	int i = 0;

	//上墙体
	for (i = 0; i < 30; i++)
	{
		wprintf(L"%lc", WALL);
	}

	//下墙体
	SetPos(0, 24);
	for (i = 0; i < 30; i++)
	{
		wprintf(L"%lc", WALL);
	}

	//左墙体
	for (i = 1; i <= 23; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}

	//右墙体
	for (i = 1; i <= 23; i++)
	{
		SetPos(58, i);
		wprintf(L"%lc", WALL);
	}
}

//打印右侧面板信息
void PrintInfo()
{
	SetPos(64, 8);
	printf("总得分：0    食物分数：10");
	SetPos(64, 12);
	printf("↑  ↓  ←  → 分别控制蛇移动");
	SetPos(65, 14);
	printf("F3为加速，F4为减速");
	SetPos(65, 15);
	printf("Esc 退出  Space 暂停");
	SetPos(65, 18);
	printf("加速后的食物分数会更高！！！");
}

//创建蛇的节点
SnakeNode* CreatSnakeNewNode()
{
	SnakeNode* newnode = (SnakeNode*)malloc(sizeof(SnakeNode));
	if (newnode == NULL)
	{
		perror("CreatSnakeNewNode()::malloc()");
	}
	newnode->x = newnode->y = 0;
	newnode->next = NULL;
	return newnode;
}

//初始化蛇的身体
void InitSnake(SnakeNode** sp)
{
	//五个节点
	if (*sp == NULL)//空链表
	{
		*sp = CreatSnakeNewNode();
		(*sp)->y = 4;
		(*sp)->x = 30;
	}
	SnakeNode* pcur = *sp;
	//尾插
	for (int i = 1; i <= 4; i++)
	{
		SnakeNode* newnode = (SnakeNode*)malloc(sizeof(SnakeNode));
		newnode->y = 4;
		newnode->x = 30 - 2 * i;
		pcur->next = newnode;
		pcur = pcur->next;
	}
	pcur->next = NULL;
}

//打印蛇的身体
void PrintSnake(SnakeNode* sp)
{
	SnakeNode* pcur = sp;
	while (pcur)
	{
		SetPos(pcur->x, pcur->y);
		wprintf(L"%lc", BODY);
		pcur = pcur->next;
	}
}

//打印食物
void PrintFood(SnakeInfo* sp)
{
	//设置食物的位置
	int x, y;

again:
	x = 2 * (rand() % 28 + 1);
	y = rand() % 23 + 1;

	//不能和蛇的身体重叠
	SnakeNode* pcur = sp->snake_head;
	while (pcur)
	{
		if (pcur->x == x && pcur->y == y)
		{
			goto again;
		}
		pcur = pcur->next;
	}

	SnakeNode* newfood = CreatSnakeNewNode();
	sp->food = newfood;
	sp->food->x = x;
	sp->food->y = y;
	sp->food->next = NULL;

	SetPos(x, y);
	wprintf(L"%lc", FOOD);
}

void CreatMap(SnakeInfo* sp)
{
	//打印墙体
	CreatWall();

	//打印右侧面板信息
	PrintInfo();

	//初始化蛇的身体
	sp->snake_head = NULL;
	InitSnake(&(sp->snake_head));

	//打印蛇的身体
	PrintSnake(sp->snake_head);

	//打印食物
	PrintFood(sp);

}

//初始化游戏
void GameStart(SnakeInfo* sp)
{
	//设置界面大小
	system("mode con cols=100 lines=30");
	system("title 贪吃蛇");

	//获取句柄
	HANDLE hOutput = NULL;
	hOutput = GetStdHandle(STD_OUTPUT_HANDLE);

	//获取光标信息
	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(hOutput, &CursorInfo);

	CursorInfo.bVisible = false; //隐藏控制台光标 
	SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态 

	//打印欢迎界面
	Welcome();

	//打印游戏界面
	CreatMap(sp);

	//设置好初始游戏参数
	sp->food_score = 10;
	sp->snake_speed = 200;
	sp->total_score = 0;
	sp->snake_diretion = RIGHT;//开始向右移动
	sp->snake_state = OK;

}

//暂停
void Stop()
{
	while (1)
	{
		Sleep(200);

		//再次按下空格跳出睡眠
		if (KEY_PRESS(VK_SPACE))
		{
			break;
		}
	}
}

//吃到食物
//蛇身体增加一节
int EatFood(SnakeInfo* sp)
{
	if ((sp->food->x == sp->snake_head->x) && (sp->food->y == sp->snake_head->y))
	{
		//吃到返回
		return 1;
	}
	//没有吃到返回0
	return 0;
}

//没有吃到食物
void NoFood(SnakeInfo* sp)
{
	SnakeNode* pcur = sp->snake_head;
	SnakeNode* prev = sp->snake_head;
	while (pcur->next)
	{
		prev = pcur;
		SetPos(pcur->x, pcur->y);
		wprintf(L"%lc", BODY);
		pcur = pcur->next;
	}
	SnakeNode* del = pcur;
	SetPos(del->x, del->y);
	printf("  ");
	free(del);
	del = NULL;
	prev->next = NULL;
}

//是否被墙撞死
void kill_by_wall(SnakeInfo* sp)
{
	if (sp->snake_head->x == 0 || sp->snake_head->x == 58 || sp->snake_head->y == 0 || sp->snake_head->y == 24)
	{
		sp->snake_state = KILL_BY_WALL;
	}
}

//是否撞到自己
void kill_by_itself(SnakeInfo* sp)
{
	SnakeNode* pcur = sp->snake_head->next;
	while (pcur)
	{
		if (sp->snake_head->x == pcur->x && sp->snake_head->y == pcur->y)
		{
			sp->snake_state = KILL_BY_ITSELF;
			break;
		}
		pcur = pcur->next;
	}
}

//蛇的移动
void SnakeMove(SnakeInfo* sp)
{
	//头插新节点
	SnakeNode* newnode = (SnakeNode*)malloc(sizeof(SnakeNode));
	newnode->next = sp->snake_head;
	sp->snake_head = newnode;

	switch (sp->snake_diretion)
	{
	case UP:
		sp->snake_head->y = sp->snake_head->next->y - 1;
		sp->snake_head->x = sp->snake_head->next->x;
		break;
	case DOWN:
		sp->snake_head->y = sp->snake_head->next->y + 1;
		sp->snake_head->x = sp->snake_head->next->x;
		break;
	case LEFT:
		sp->snake_head->x = sp->snake_head->next->x - 2;//注意x轴
		sp->snake_head->y = sp->snake_head->next->y;
		break;
	case RIGHT:
		sp->snake_head->x = sp->snake_head->next->x + 2;
		sp->snake_head->y = sp->snake_head->next->y;
		break;
	}

	//判断新状态
	if (EatFood(sp))
	{
		PrintSnake(sp->snake_head);
		sp->total_score += sp->food_score;
		//释放食物的节点
		free(sp->food);
		//打印新的食物
		PrintFood(sp);
	}
	else 
	{
		NoFood(sp);
		kill_by_wall(sp);
		kill_by_itself(sp);
	}
}

//游戏运行
void GameRun(SnakeInfo* sp)
{
	do
	{
		SetPos(64, 8);
		printf("总得分：%2d   食物分数：%2d", sp->total_score, sp->food_score);
		if (KEY_PRESS(VK_UP) && sp->snake_diretion != DOWN)
		{
			//上移
			sp->snake_diretion = UP;
		}
		else if (KEY_PRESS(VK_DOWN) && sp->snake_diretion != UP)
		{
			//下移
			sp->snake_diretion = DOWN;
		}
		else if(KEY_PRESS(VK_RIGHT) && sp->snake_diretion != LEFT)
		{
			//右移
			sp->snake_diretion = RIGHT;
		}
		else if (KEY_PRESS(VK_LEFT) && sp->snake_diretion != RIGHT)
		{
			//左移
			sp->snake_diretion = LEFT;
		}
		else if (KEY_PRESS(VK_SPACE))
		{
			//暂停
			Stop();
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			//正常退出游戏
			sp->snake_state = END_NORMAL;
		}
		else if (KEY_PRESS(VK_F3))
		{
			//加速
			//每次加速二十
			//可以加速到100ms
			if (sp->snake_speed > 100)
			{
				sp->snake_speed -= 20;
				sp->food_score += 2;
			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			//减速
			//每次减速二十
			//可以减速到300ms
			if (sp->snake_speed < 280)
			{
				sp->snake_speed += 20;
				sp->food_score -= 2;
			}
		}
		//睡眠时间
		Sleep(sp->snake_speed);
		//蛇的移动
		SnakeMove(sp);
	} while (sp->snake_state == OK);

	//打印死亡或者结束信息
	if (sp->snake_state == END_NORMAL)
	{
		SetPos(30, 10);
		printf("您退出了游戏！！！");
	}
	else if (sp->snake_state == KILL_BY_WALL)
	{
		SetPos(30, 10);
		printf("您的蛇被墙撞到了！！！");
	}
	else
	{
		SetPos(30, 10);
		printf("您的蛇撞到了自己！！！");
	}
}

//游戏结束
void GameEnd(SnakeInfo* sp)
{
	//销毁蛇节点
	SnakeNode* pcur = sp->snake_head;
	while (pcur)
	{
		SnakeNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	sp->snake_head = NULL;

	//销毁食物节点
	free(sp->food);
	sp->food = NULL;
}